package com.phantasy.net.connection
{
	import com.phantasy.Message;
	import com.phantasy.globals.GameParameters;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.ObjectEncoding;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.utils.getDefinitionByName;

	/**
	 *网络连接器 
	 * @author Liseen
	 * 
	 */	
	public class Connection extends EventDispatcher
	{
		public static const CONNECTED:int = 0;
		public static const CONNECTING:int = 1;
		public static const CLOSE:int = 2;
		public static const FIRST_CONNECT:int = 0;
		public static const RE_CONNECT:int = 1;
		
		private static var instance:Connection;
		
		private var _listeners:Dictionary;
		private var _socket:Socket;
		private var _state:int;//当前连接状态
		private var _connectType:int//连接类型
		private var _lastData:ByteArray;
		private var _totalData:ByteArray;
		private var _voData:ByteArray;
		private var _voLength:int=0;
		private var _isZIP:int;
		private var id:int = 0x7000;
		
		public var isReConnect:Boolean = false;
		public var handlerClose:Function;
		public var handlerIoError:Function;
		public var handlerSecurityError:Function;
		public var handlerSuccess:Function;
		public var reHandlerSuccess:Function;
		
		public var host:String;
		public var port:int;
		public function Connection(value:Singleton)
		{
			this._lastData = new ByteArray();
			this._totalData = new ByteArray();
			this._voData = new ByteArray();
			
			_listeners = new Dictionary();
			_socket = new Socket();
			this._socket.objectEncoding = ObjectEncoding.AMF3;
			this._socket.timeout = 5000;
			this._socket.addEventListener(Event.CLOSE, this.closeHandler);
			this._socket.addEventListener(Event.CONNECT, this.connectHandler);
			this._socket.addEventListener(IOErrorEvent.IO_ERROR, this.ioErrorHandler);
			this._socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.securityErrorHandler);
			this._socket.addEventListener(ProgressEvent.SOCKET_DATA, this.onSocketData);
			this.state = Connection.CLOSE;
		}
		/**
		 *获取网络连接器实例 
		 * @return 
		 * 
		 */		
		public static function getInstance():Connection
		{
			if(Connection.instance==null){
				Connection.instance = new Connection(new Singleton());
			}
			
			return Connection.instance;
		}
		
		/**
		 *设置当前网络连接状态 
		 * @param value
		 * 
		 */		
		public function set state(value:int):void
		{
			if (value != this._state)
			{
				this._state = value;
			}
		}
		/**
		 *获取当前网络连接状态 
		 * @return 
		 * 
		 */		
		public function get state():int
		{
			return this._state;
		}
		
		/**
		 *开始连接 
		 * @param state	 状态
		 * 
		 */		
		public function startConnect(state:int=0):void
		{
			if((this.state!=Connection.CONNECTED)&&(this.state!=Connection.CONNECTING)){
				this._connectType = state;
				this.host = GameParameters.getInstance().gateway.host;
				this.port = GameParameters.getInstance().gateway.port;
				this._socket.connect(this.host,this.port);
				this.state = Connection.CONNECTING;
			}
		}
		
		/**
		 *发送消息 
		 * @param message
		 * 
		 */		
		public function sendMessage(message:Message):void
		{	
			if(this.state==Connection.CONNECTED){
				this.send(message);
				
			}
		}
		/**
		 *添加Socket侦听 
		 * @param type		类型
		 * @param callBack	回调
		 * 
		 */		
		public function addSocketListener(type:String,callBack:Function):void
		{
			var list:Array = this._listeners[type];
			if (list == null)
			{
				list = [];
				this._listeners[type] = list;
			}
			else
			{
				//如果数组内有对应的方法就return
				if (list.indexOf(callBack) != -1)
				{
					return;
				}
			}
			//最终到数组中
			list.push(callBack);	
		}
		
		
		/**
		 *移除Socket侦听 
		 * @param type
		 * @param callBack
		 * 
		 */			
		public function removeSocketListener(type:String, callBack:Function):void
		{
			var index:int;
			var list:Array = this._listeners[type];
			if (list)
			{
				index = list.indexOf(callBack);
				if (index != -1)
				{
					list.splice(index, 1);
				}
			}
		}
		
		public function resultHandler(methodName:String,message:Message):void
		{
			var fun:Function;
			methodName = methodName.toUpperCase();
			var funList:Array = this._listeners[methodName];
			for each (fun in funList)
			{
				fun.apply(null, [message]);
			}
		}
		
		/**
		 *服务器关闭 
		 * @param event
		 * 
		 */		
		private function closeHandler(event:Event):void
		{
			this.state = Connection.CLOSE;
			if (this.handlerClose != null)
			{
				this.handlerClose();
			}
		}
		/**
		 *连接上 
		 * @param event
		 * 
		 */		
		private function connectHandler(event:Event):void
		{
			this.state = Connection.CONNECTED;
			startHandshark();
			switch(this._connectType){
				case Connection.FIRST_CONNECT:
					if(this.handlerSuccess!=null){
						this.handlerSuccess();
					}
				break;
				case Connection.RE_CONNECT:
					if(this.reHandlerSuccess!=null){
						this.reHandlerSuccess();
					}
				break;
			}
		}
		/**
		 *连接服务器IO报错 
		 * @param event
		 * 
		 */		
		private function ioErrorHandler(event:IOErrorEvent):void
		{
			this.state = Connection.CLOSE;
			if(this.handlerIoError!=null){
				this.handlerIoError();
			}
		}
		/**
		 *连接服务器安全沙箱报错 
		 * @param event
		 * 
		 */		
		private function securityErrorHandler(event:SecurityErrorEvent):void
		{
			this.state = Connection.CLOSE;
			if(this.handlerSecurityError!=null){
				this.handlerSecurityError();
			}
		}
		
		private function onSocketData(event:ProgressEvent):void
		{
			var totalDataPosition:int;
			if(this._socket.bytesAvailable>0){
				this._totalData.clear();
				if (this._lastData.bytesAvailable > 0)
				{
					this._lastData.position = 0;
					this._totalData.writeBytes(this._lastData, 0, this._lastData.bytesAvailable);
					this._lastData.clear();
				}
				this._socket.readBytes(this._totalData, this._totalData.length, this._socket.bytesAvailable);
				this._totalData.position = 0;
				while (this._totalData.bytesAvailable >= 5)
				{
					this._voLength = this._totalData.readInt();
					if (this._totalData.bytesAvailable < this._voLength)
					{
						this._totalData.position = (this._totalData.position - 4);
						this._totalData.readBytes(this._lastData, 0, this._totalData.bytesAvailable);
						break;
					}
					this._isZIP = this._totalData.readUnsignedByte();
					this._voData.clear();
					if (this._voLength > 1)
					{
						this._totalData.readBytes(this._voData, 0, (this._voLength - 1));
						if (this._isZIP == 1)
						{
							this._voData.uncompress();
						}
					}
					this._lastData.clear();
					totalDataPosition = this._totalData.position;
					this._totalData.readBytes(this._lastData, 0, this._totalData.bytesAvailable);
					this._totalData.position = totalDataPosition;
					this.decodeData(this._voData);
				}
				this._totalData.clear();
			}
		}
		
		private function startHandshark():void
		{
			var by:ByteArray = new ByteArray();
			by.length = 23;
			this._socket.writeBytes(by);
			this._socket.flush();
		}
		
		
		private function send(ms:Message):void
		{
			var dataLen:int;
			if(ms==null){
				return;
			}
			
			var sendData:ByteArray = new ByteArray();
			sendData.position=0;
			sendData = Message.encode(ms);
			if (sendData.length > 0x0200)
			{
				sendData.compress();
				this.id = 1;
			}
			else
			{
				this.id = 0;
			}
			dataLen = (1 + sendData.length);
			this._socket.writeInt(dataLen);
			this._socket.writeByte(this.id);
			this._socket.writeBytes(sendData);
			this._socket.flush();
			
		}
		/**
		 *解码数据 
		 * @param value
		 * 
		 */		
		private function decodeData(value:ByteArray):void
		{
			var classDef:Class;
			var handleMessage:Message;
			value.readUnsignedByte();
			value.readUnsignedByte();
			value.readUnsignedByte();
			value.readUnsignedByte();
			var name:String = value.readUTF();
			var className:String = "com.phantasy.proto" + "." + name;
			classDef = getDefinitionByName(className) as Class;
			handleMessage = new classDef();
			value.position = 0;
			handleMessage.decode(value);
			this.resultHandler(handleMessage.getMethodName(), handleMessage);
		}
	}
}
class Singleton 
{
	
	
}